﻿using HtmlAgilityPack;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Domain.Common.Site
{
    using SoccerBet2014.Common.Json;
    using Component;
    using Domain.Models.Site;

    public partial class IbcSessionManager : SessionManagerBase
    {
        protected override bool CheckLoginHtmlState(string html)
        {
            if (html.Contains("識別碼錯誤"))
            {
                LoginState = LoginState.CodeError;
                return false;
            }
            if (html.Contains("帳號/密碼錯誤"))
            {
                LoginState = LoginState.UserNameOrPasswordError;
                return false;
            }
            if (html.Contains("帳號已關閉"))
            {
                LoginState = LoginState.UserClose;
                _loginInfo.IsLogin = false;
                return false;
            }
            if (html.Contains("密碼已過期"))
            {
                LoginState = LoginState.NeedChangePassword;
                return false;
            }
            if (html.Contains("登錄次數過於頻繁"))
            {
                LoginState = LoginState.LoginTimesTooOften;
                return false;
            }
            if (html.Contains("登入已失效") || html.Contains("top.window.location.href=") || html.Contains("Forced to logout"))
            {
                LoginState = LoginState.BeenSignedOut;
                _loginInfo.IsLogin = false;
                return false;
            }
            if (html.Contains("進行維護") || html.IndexOf("server maintenance", 0, StringComparison.OrdinalIgnoreCase) != -1)
            {
                LoginState = LoginState.WebSiteMaintenance;
                _loginInfo.IsLogin = false;
                return false;
            }
            return true;
        }

        protected override bool ValidHostIsWWW(string host)
        {
            string wwwHost = string.Format("www.{0}", GlobalHost);
            return host.Equals(wwwHost, StringComparison.OrdinalIgnoreCase);
        }

        protected override string WWWHost()
        {
            return string.Format("www.{0}", GlobalHost);
        }

        #region password encrypt
        public virtual string Md5EncryptPassword(string password, string token)
        {
            MD5 md5 = MD5.Create();
            var str = IbcEncryptPassword(password);
            var buffer = Encoding.GetBytes(str + token);
            var md5Buffer = md5.ComputeHash(buffer);
            return BytesToHexString(md5Buffer);
        }

        protected virtual string BytesToHexString(byte[] bytes)
        {
            StringBuilder sb = new StringBuilder(bytes.Length);
            for (int i = 0; i < bytes.Length; i++)
            {
                var hexStr = bytes[i].ToString("x");
                if (hexStr.Length < 2)
                    sb.Append('0');
                sb.Append(hexStr);
            }
            return sb.ToString();
        }

        protected virtual string IbcEncryptPassword(string password)
        {
            var len = 30;
            var padPassStr = password.PadRight(len, (char)21);
            double numPass = 1;
            for (int i = 0; i < len; i++)
            {
                int val = len + (i + 1) * (int)padPassStr[i];
                numPass = numPass * val;
            }
            var numStr = numPass.ToString("E14").ToUpper();
            var resetNumStr = numStr.Replace("E+0", "E+");

            StringBuilder sbBuffer = new StringBuilder();
            for (var i = 0; i < resetNumStr.Length; i++)
            {
                string int4Val = "";
                if (i == resetNumStr.Length - 1)
                    int4Val = ((int)resetNumStr[i]).ToString();
                else if (i == resetNumStr.Length - 2)
                    int4Val = string.Format("{0}{1}", (int)resetNumStr[i], (int)resetNumStr[i + 1]);
                else
                    int4Val = string.Format("{0}{1}{2}", (int)resetNumStr[i], (int)resetNumStr[i + 1], (int)resetNumStr[i + 2]);
                sbBuffer.Append(Convert.ToInt32(int4Val).ToString("x"));
            }

            var result = new StringBuilder();
            for (var b = 20; b <= sbBuffer.Length - 18; b += 2)
            {
                result.Append(sbBuffer[b - 1]);
            }
            return result.ToString().ToUpper();
        }
        #endregion

        #region resolve html paramter
        protected virtual void ResolveTodayFormParamters(string html)
        {
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            var liveForm = doc.DocumentNode.SelectSingleNode("//form[@name='DataForm_L']");
            var noLiveForm = doc.DocumentNode.SelectSingleNode("//form[@name='DataForm_D']");

            if (liveForm != null)
            {
                LiveFormParamters = new OddsDataFormParamters();
                LiveFormParamters.Market = "l";
                LiveFormParamters.Sport = 1;
                LiveFormParamters.OddsType = 4;
                LiveFormParamters.Key = "lodds";
                var lvnodes = liveForm.SelectNodes("//input");
                foreach (var node in lvnodes)
                {
                    var name = node.GetAttributeValue("name", "");
                    var value = node.GetAttributeValue("value", "");
                    if (name.StartsWith("k") && value.StartsWith("v"))
                    {
                        LiveFormParamters.ExtKey = name;
                        LiveFormParamters.ExtValue = value;
                        FormRequestExtKey.Value = name;
                        FormRequestExtValue.Value = value;
                    }
                    if (name == "MainLeague")
                    {
                        int mainLeague = 0;
                        int.TryParse(value, out mainLeague);
                        LiveFormParamters.MainLeague = mainLeague;
                    }
                }
            }
            if (noLiveForm != null)
            {
                NoLiveFormParamters = new OddsDataFormParamters();
                NoLiveFormParamters.Market = "t";
                NoLiveFormParamters.Sport = 1;
                NoLiveFormParamters.OddsType = 4;
                NoLiveFormParamters.Key = "dodds";
                var nlnodes = noLiveForm.SelectNodes("//input");
                foreach (var node in nlnodes)
                {
                    var name = node.GetAttributeValue("name", "");
                    var value = node.GetAttributeValue("value", "");
                    if (name.StartsWith("k") && value.StartsWith("v"))
                    {
                        NoLiveFormParamters.ExtKey = name;
                        NoLiveFormParamters.ExtValue = value;
                        FormRequestExtKey.Value = name;
                        FormRequestExtValue.Value = value;
                    }
                    if (name == "DispRang")
                    {
                        NoLiveFormParamters.DispRang = value;
                    }
                    if (name == "MainLeague")
                    {
                        int mainLeague = 0;
                        int.TryParse(value, out mainLeague);
                        NoLiveFormParamters.MainLeague = mainLeague;
                    }
                }
            }
        }

        protected virtual void ResolveEarlyFormParamters(string html)
        {
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            var noLiveForm = doc.DocumentNode.SelectSingleNode("//form[@name='DataForm_D']");
            if (noLiveForm != null)
            {
                EarlyFormParamters = new OddsDataFormParamters();
                EarlyFormParamters.Market = "e";
                EarlyFormParamters.Sport = 1;
                EarlyFormParamters.RT = "W";
                EarlyFormParamters.OddsType = 4;
                EarlyFormParamters.OrderBy = 1;
                var nlnodes = noLiveForm.SelectNodes("//input");
                foreach (var node in nlnodes)
                {
                    var name = node.GetAttributeValue("name", "");
                    var value = node.GetAttributeValue("value", "");
                    if (name.StartsWith("k") && value.StartsWith("v"))
                    {
                        EarlyFormParamters.ExtKey = name;
                        EarlyFormParamters.ExtValue = value;
                    }
                    if (name == "DispRang")
                    {
                        EarlyFormParamters.DispRang = value;
                    }
                    if (name == "DT")
                    {
                        EarlyFormParamters.DT = System.Web.HttpUtility.UrlEncode(value);
                    }
                }
            }
        }
        #endregion

        #region resolve string
        protected DateTime? ResolveRegexTime(Regex regex, string group, string matchString)
        {
            var mat = regex.Match(matchString);
            if (mat.Success)
            {
                if (mat.Groups[group].Success)
                {
                    DateTime matTime;
                    if (DateTime.TryParse(mat.Groups[group].Value, out matTime))
                        return matTime;
                }
            }
            return null;
        }

        protected virtual decimal ResolveHandicap(string orginHandicap, string focus)
        {
            decimal handicap = 0m;
            if (orginHandicap.IndexOf('-') > -1)
            {
                var handicapArr = orginHandicap.Split('-');
                if (handicapArr.Length > 1)
                {
                    decimal a, b;
                    decimal.TryParse(handicapArr[0], out a);
                    decimal.TryParse(handicapArr[1], out b);
                    handicap = (a + b) / 2;
                }
            }
            else
            {
                decimal.TryParse(orginHandicap, out handicap);
            }
            if (focus.Equals("a")) handicap = handicap * -1;
            return handicap;
        }

        protected virtual DateTime? ResolveKickoffTime(string kickoffTime)
        {
            if (kickoffTime.Length == 12)
            {
                string formatTime = string.Format("{0}/{1}/{2} {3}:{4}:00"
                    , kickoffTime.Substring(0, 4)
                    , kickoffTime.Substring(4, 2)
                    , kickoffTime.Substring(6, 2)
                    , kickoffTime.Substring(8, 2)
                    , kickoffTime.Substring(10, 2));

                DateTime gtime = DateTime.MinValue;
                if (DateTime.TryParse(formatTime, out gtime))
                    return gtime.AddHours(12);
            }
            return null;
        }
        #endregion

        #region get json value
        protected virtual T MappingJsonValue<T>(JArray json, Dictionary<string, int> mapping, string field, T def) where T : struct
        {
            if (!mapping.ContainsKey(field))
                return def;
            var index = mapping[field];
            if (json.Count < index) return def;
            return json[index].TryCaseValue<T>(def);
        }
        protected virtual string MappingJsonValue(JArray json, Dictionary<string, int> mapping, string field, string def)
        {
            if (!mapping.ContainsKey(field))
                return def;
            var index = mapping[field];
            if (json.Count < index) return def;
            return json[index].Value<string>();
        }
        protected virtual T EventJsonValue<T>(JArray json, string field, T def) where T : struct
        {
            if (!GameEventIndexMapping.ContainsKey(field))
                return def;
            var index = GameEventIndexMapping[field];
            if (json.Count < index)
                return def;
            if (!json[index].CanString())
                return def;

            return json[index].TryCaseValue<T>(def);
        }
        protected virtual string EventJsonValue(JArray json, string field, string def)
        {
            if (!GameEventIndexMapping.ContainsKey(field))
                return def;
            var index = GameEventIndexMapping[field];
            if (json.Count < index)
                return def;
            if (!json[index].CanString())
                return def;

            return json[index].Value<string>();
        }

        protected virtual T EventOddsJsonValue<T>(JArray json, Dictionary<string, int> mapping, int start, string field, T def) where T : struct
        {
            if (!mapping.ContainsKey(field))
                return def;
            var index = mapping[field];
            var realIndex = index + start;
            if (json.Count < realIndex)
                return def;
            if (!json[realIndex].CanString())
                return def;
            return json[realIndex].TryCaseValue<T>(def);
        }
        protected virtual string EventOddsJsonValue(JArray json, Dictionary<string, int> mapping, int start, string field, string def)
        {
            if (!mapping.ContainsKey(field))
                return def;
            var index = mapping[field];
            var realIndex = index + start;
            if (json.Count < realIndex)
                return def;
            if (!json[realIndex].CanString())
                return def;
            return json[realIndex].Value<string>();
        }
        #endregion

        public async override Task<bool> CheckSession()
        {
            string url = string.Format(@"http://{0}/LeftAllInOne.aspx", Host);
            var reqTask = GetHtmlRequest(new Uri(url));
            var result = await reqTask;
            if (string.IsNullOrEmpty(result))
            {
                _loginInfo.IsLogin = false;
                _loginInfo.LastRequestTime = DateTime.Now;
                LoginState = LoginState.BeenSignedOut;
                return false;
            }
            if (!CheckLoginHtmlState(result))
                return false;
            return true;
        }

        public async override Task StartHeartbeat()
        {
            string url = string.Format(@"http://{0}/login_checkin.aspx", Host);
            string referer = string.Format(@"http://{0}/topmenu.aspx", Host);
            var reqTask = GetHtmlRequest(new Uri(url), referer);
            await reqTask;
        }

        public async override Task<bool> Signout()
        {
            return true;
        }
    }
}
